home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-01-18 | 22.3 KB | 877 lines | [TEXT/MMCC] |
- //
- // CTCPEndpoint.cp
- //
- // TurboTCP library
- // TCP generic protocol interpreter
- //
- // Copyright © 1993-95, FrostByte Design / Eric Scouten
- //
-
- #include "CTCPEndpoint.h"
-
- #include "CTCPDriver.h"
- #include "CTCPResolverCall.h"
- #include "CTCPStream.h"
-
- #include <string.h>
-
-
- #if TurboTCP_TCL
- #include "CDecorator.h"
- #include "CError.h"
- #include "Constants.h"
- #include "Global.h"
- #include "TBUtilities.h"
- #include "TCLUtilities.h"
-
- #define ConcatPStr ConcatPStrings
- #define CopyPStr CopyPString
-
- extern CDecorator* gDecorator;
- extern CError* gError;
-
- TCL_DEFINE_CLASS_M0(CTCPEndpoint);
- #endif
-
- #if TurboTCP_PP
- #include "String_Utils.h"
- #include "UWindows.h"
- #endif
-
-
-
- //***********************************************************
- //
- // Shortcut macros for session status information.
- //
-
- #define sessionOpen (cState >= strSYNReceived) && (cState <= strTimeWait)
- #define sessionOpening (cState >= strListen) && (cState <= strSYNSent)
- #define sessionClosing (cState >= strFINWait1) && (cState <= strTimeWait)
-
-
- //***********************************************************
-
- CTCPEndpoint::CTCPEndpoint
- (unsigned short theDefaultPort, // default IP port number (the only required parameter)
- unsigned long recBufferSize, // size of receive buffer to create
- unsigned short autoReceiveSize, // number of entries in RDS for auto receive
- unsigned short autoReceiveNum, // number of auto-receive calls to issue at once
- Boolean doUseCName) // true to get canonical name of remote host
- // whenever possible
-
- {
-
- // blank out the usual variables
-
- itsStream = nil;
- itsResolver = nil;
- pendingOpenByName = closeAndQuit = false;
- actualPort = defaultPort = theDefaultPort;
- hostCName[0] = '\0';
- useCName = doUseCName;
- goAwayOnClose = false;
- showFileName = true;
- showHostName = true;
- untitledNumber = 0;
- localPort = 0;
-
-
- // create a TCP stream
-
- Try_ {
- itsStream = new CTCPStream(*this, recBufferSize, autoReceiveSize, autoReceiveNum);
- itsResolver = new CTCPResolverCall(*this);
- }
-
- Catch_(err) {
- if (itsStream)
- itsStream->Dispose();
- itsStream = nil;
- if (itsResolver)
- itsResolver->Dispose();
- itsResolver = nil;
- ThrowSame_;
- }
-
- EndCatch_;
-
- }
-
-
- //***********************************************************
-
- CTCPEndpoint::~CTCPEndpoint()
-
- {
- if (itsStream)
- itsStream->Dispose();
- if (itsResolver)
- itsResolver->Dispose();
- }
-
-
- // -- opening/closing session --
-
- //***********************************************************
-
- void CTCPEndpoint::OpenUserHost
- (char* theHostName, // the host name (and optional port#) string
- unsigned short theDefaultPort, // the default port number
- Boolean allowPortChange) // true to allow port number overrides
-
- // Open a connection to the host named by the string. Hostname may be either dotted
- // decimal notation or a DNS name. A port number may be specified at the end of the
- // hostname (separated by a single space) if the caller permits it.
-
- {
- Str255 portNumStr;
- long newPortNum = 0;
- register char* s;
- register char* d;
- register short i;
-
-
- // ensure that there is a host to open
-
- if (*theHostName == '\0') {
- HandleOpenFailed(nameSyntaxErr);
- return;
- }
-
-
- // parse for port number
-
- pendingPortNumber = theDefaultPort;
- if (allowPortChange) {
- s = theHostName;
- d = (char*) &hostCName;
- while ((*s != ' ') & (*s != '\0'))
- *d++ = *s++;
- *d = '\0';
- if (*s == ' ') {
- d = (char*) &portNumStr;
- i = -1;
- while (*s != '\0')
- *d++ = *s++, i++;
- portNumStr[0] = (char) i;
- if (i > 0)
- ::StringToNum(portNumStr, &newPortNum);
- }
- }
- else
- ::BlockMoveData(theHostName, &hostCName, 255);
-
- if (newPortNum != 0)
- pendingPortNumber = newPortNum;
-
-
- // issue resolver command
-
- pendingOpenByName = true;
- actualPort = pendingPortNumber;
- itsResolver->DoStrToAddr((char*) &hostCName);
-
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::OpenHost
- (unsigned long remoteHostIP, // the remote host’s address
- unsigned short remoteHostPort) // the remote host’s port number
-
- // Open a connection to the host named by the IP address and port number specified.
- // This routine provides a way to bypass the DNR when opening connections. If the
- // doUseCName option was specified when the object was constructed, this method will
- // issue a DNR call to obtain the remote host’s canonical name.
-
- {
-
- // open the connection
-
- pendingOpenByName = true;
- itsStream->OpenConnection(false, remoteHostIP, remoteHostPort, localPort);
-
-
- // set window title to dotted IP address until we can get the canonical name
-
- itsResolver->DoAddrToStr(remoteHostIP, hostCName);
- StateChanged();
-
-
- // if desired, get the canonical name
-
- if (useCName) {
- itsResolver->DoAddrToName(hostAddress);
- pendingOpenByName = true;
- }
-
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::Listen
- (unsigned long remoteHostIP, // the remote host’s address
- unsigned short remoteHostPort) // the remote host’s port number
-
- // Initiate a “passive open” command. Wait for a remote host to attempt to connect to this
- // machine. If the connection is to come from a specific host, the host’s IP address must be
- // specified and the port number may be specified. If the connection may come from any host,
- // both the IP address and port number must be left blank.
-
- {
- pendingOpenByName = true;
- itsStream->OpenConnection(true, remoteHostIP, remoteHostPort, localPort);
- }
-
-
- //***********************************************************
-
- Boolean CTCPEndpoint::LocalClose
- (Boolean quitting) // true if the application is quitting
-
- // Use this method when the user clicks the close box, or your application is done with
- // this session. Ensures that the TCP stream is gracefully closed. If not quitting, delays
- // closure until MacTCP says the stream has been terminated. Note that this method
- // has the same interface as TCL’s CDirector::Close.
-
- // Returns false if close/quit should be aborted.
-
- // This method is not called by the CTCPEndpoint class and need not be overriden.
-
- {
- Boolean hadSessionOpen;
- unsigned short cState;
-
-
- // if session is open, give time for session to close
-
- if (itsStream) {
- cState = itsStream->ConnectionState();
- hadSessionOpen = sessionOpen;
- if (sessionOpen)
- itsStream->Close();
- if (sessionOpening)
- itsStream->Abort();
- }
- else
- hadSessionOpen = false;
-
-
- // close the document – maybe?
-
- return quitting || !hadSessionOpen || !goAwayOnClose;
-
- }
-
-
- //***********************************************************
-
- Boolean CTCPEndpoint::SessionEstablished()
-
- // Returns true if a TCP session is currently established and ready for data.
-
- {
- if (itsStream)
- return (itsStream->ConnectionState() == strEstablished);
- else
- return false;
- }
-
-
- // -- sending data --
-
- //***********************************************************
-
- void CTCPEndpoint::SendBfrCpy
- (const void* theData, // the data to send
- unsigned short theDataSize) // number of bytes at this address
-
- // Send bytes to the TCP stream. Copies the data to a buffer which can persist beyond the
- // method or object which generated the data.
-
- {
- itsStream->SendBfrCpy(theData, theDataSize);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SendBfrNoCpy
- (const void* theData, // the data to send
- unsigned short theDataSize, // number of bytes to send
- Boolean disposeWhenDone, // true to dispose of the buffer (DisposePtr)
- // when completed (only if this pointer was
- // allocated as a pointer, not a handle deref)
- Boolean notifyWhenDone) // true to notify the endpoint object
- // when completed (via HandleDataSent or
- // HandleSendFailed)
-
- // Send bytes to the TCP stream. Does not copy the data. The caller is responsible for
- // ensuring that the data are available until the call is completed. You may detect the safe
- // transmittal of your data by overriding the HandleDataSent() method and comparing
- // pointers. You may also request that the data be disposed (using the DisposePtr routine)
- // when the call completes by setting disposeWhenDone to true.
-
- {
- itsStream->SendBfrNoCpy(theData, theDataSize, disposeWhenDone, notifyWhenDone);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SendChar
- (const char theChar) // the character to send
-
- // Send a single character to the remote host.
-
- {
- SendBfrCpy(&theChar, 1);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SendCString
- (const char* theString) // the string to send
-
- // Send a C string to the remote host (not including the /0 byte).
-
- {
- SendBfrCpy(theString, strlen((char*) theString));
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SendPString
- (const unsigned char* theString) // the string to send
-
- // Send a Pascal string to the TCP stream (not including the length byte).
-
- {
- SendBfrCpy((char*) theString + 1, theString[0]);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SetNextPush()
-
- // The next data will be sent with the “push” flag set.
-
- {
- itsStream->SetNextPush();
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::SetNextUrgent()
-
- // The next data will be sent with the “urgent” flag set.
-
- {
- itsStream->SetNextUrgent(false);
- }
-
-
- //***********************************************************
- //
- // Stream-like manipulators for CTCPEndpoint
- //
-
- CTCPEndpoint& endl(CTCPEndpoint& ep) // send end-of-line characters
- {
- ep.SendChar('\r');
- return ep;
- }
-
- CTCPEndpoint& local_IP(CTCPEndpoint& ep) // send local IP address in dotted IP format
- {
- char ip_addr[16];
- UTurboTCP::DoAddrToStr(ep.GetLocalHostIP(), ip_addr);
- ep << ip_addr;
- return ep;
- }
-
- CTCPEndpoint& remote_IP(CTCPEndpoint& ep) // send remote IP address in dotted IP format
- {
- char ip_addr[16];
- UTurboTCP::DoAddrToStr(ep.GetRemoteHostIP(), ip_addr);
- ep << ip_addr;
- return ep;
- }
-
- CTCPEndpoint& push(CTCPEndpoint& ep) // send next data with “push” flag set
- {
- ep.SetNextPush();
- return ep;
- }
-
- CTCPEndpoint& urgent(CTCPEndpoint& ep) // send next data with “urgent” flag set
- {
- ep.SetNextUrgent();
- return ep;
- }
-
-
-
- //***********************************************************
- //
- // Configuration methods
- //
- // Use these methods to set various IP parameters for the next connection built on this
- // endpoint. If a session has been opened already on this endpoint, these methods will have
- // no effect on the current session.
- //
-
- void CTCPEndpoint::SetDefaultPort(unsigned short newDefaultPort) // set default remote host port
- { defaultPort = newDefaultPort; }
- void CTCPEndpoint::SetLocalHostPort(unsigned short newLocalPort) // set a new local host port for next connection
- { localPort = newLocalPort; }
- void CTCPEndpoint::SetOpenTimeout(unsigned short openMaxSeconds) // max time to establish session for next connection
- { itsStream->SetULPTimeoutValue(openMaxSeconds); }
- void CTCPEndpoint::SetListenTimeout(unsigned short openMaxSeconds) // max time to wait for passive connection
- { itsStream->SetCommandTimeout((short) openMaxSeconds); }
- void CTCPEndpoint::SetTypeOfService(Boolean lowDelay, // set type of service for next connection
- Boolean highThroughput, Boolean highReliability)
- { itsStream->SetTypeOfService((lowDelay ? 0x01 : 0) |
- (highThroughput ? 0x02 : 0) | (highReliability ? 0x04 : 0)); }
- void CTCPEndpoint::SetPrecedence(TCPPrecedence newPrecedence) // set precedence for next connection
- { itsStream->SetPrecedence((short) newPrecedence); }
- void CTCPEndpoint::SetDontFragment(Boolean newDontFragment) // set/clear don’t fragment for next connection
- { itsStream->SetDontFrag((short) newDontFragment); }
- void CTCPEndpoint::SetTimeToLive(unsigned short newTimeToLive) // maximum hop count for IP connection
- { itsStream->SetTimeToLive(newTimeToLive); }
- void CTCPEndpoint::SetSecurity(unsigned short newSecurity) // security flag
- { itsStream->SetSecurity(newSecurity); }
-
-
- //***********************************************************
- //
- // Selectors
- //
- // Use these methods to get information on the current endpoint and connection.
- //
-
- void CTCPEndpoint::GetRemoteHostName(char* hostStringBfr) // get the name of the remote host machine
- { ::BlockMoveData(hostCName, hostStringBfr, strlen(hostCName)); }
- unsigned long CTCPEndpoint::GetRemoteHostIP()
- { return hostAddress; }
- unsigned short CTCPEndpoint::GetRemoteHostPort()
- { return actualPort; }
- unsigned long CTCPEndpoint::GetLocalHostIP()
- { return UTurboTCP::GetLocalIPAddr(); }
- unsigned short CTCPEndpoint::GetLocalHostPort()
- { return localPort; }
- unsigned short CTCPEndpoint::GetDefaultPort()
- { return defaultPort; }
-
-
- // -- state change notifications --
-
- //***********************************************************
-
- void CTCPEndpoint::RemoteClose() // protected method
-
- // This method is called when the remote host closes or aborts a session. Override this
- // to ensure that your windows are closed or the user is somehow notified. (Be sure to
- // call this method as well.)
-
- {
- unsigned short cState;
-
-
- // be sure to acknowledge the remote host’s intention to close
-
- if (itsStream) {
- cState = itsStream->ConnectionState();
- if (sessionOpen)
- itsStream->Close();
- if (sessionOpening)
- itsStream->Abort();
- }
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::StateChanged() // protected method
-
- // Called by other methods when the connection state changes (i.e. opens, aborts, or closes).
- // Builds a new window title, then calls the SetWindowTitle() method to signal that the
- // window’s title should be changed.
-
- {
- Str255 fileStr = "\p";
- Str255 hostStr = "\p";
- Str255 tempStr = "\p";
- Boolean sessionReady = SessionEstablished();
-
-
- // build document name string (if requested)
-
- if (showFileName) {
-
- // see if there’s a default name for this file
-
- GetFileName(fileStr);
-
- // if there’s no title, build an untitled-__ name
-
- if (fileStr[0] == '\0') {
- if (!untitledNumber) {
-
- // need to assign an untitled number: use class library’s default method
-
- #if TurboTCP_TCL
- untitledNumber = gDecorator->GetWCount();
- #else
- #if TurboTCP_PP
-
- // try default name “untitled”
-
- ::GetIndString(fileStr, STR__WindowStrings, Wstr_UntitledNoNumber);
-
- // if an existing window has the current name; increment counter and try again
-
- while (UWindows::FindNamedWindow(fileStr) != nil) {
- untitledNumber++;
- ::GetIndString(fileStr, STR__WindowStrings, Wstr_Untitled);
- ::NumToString(untitledNumber, tempStr);
- ConcatPStr(fileStr, tempStr);
- }
- if (untitledNumber == 0)
- untitledNumber = -1;
-
- #else
- #error Don't know how to build untitled numbers
- #endif
- #endif
- }
-
- // now we have a number, use it
-
- ::GetIndString(fileStr, STR__WindowStrings, Wstr_Untitled);
- if (untitledNumber != -1) {
- ::NumToString(untitledNumber, tempStr);
- ConcatPStr(fileStr, tempStr);
- }
- }
- }
-
-
- // build host name string
-
- if (showHostName) {
- if (!hostCName[0])
- ::GetIndString(hostStr, STR__WindowStrings, Wstr_NoSession);
- else {
-
- if (!sessionReady) // left “(” if not ready
- ::GetIndString(hostStr, STR__WindowStrings, Wstr_NotReadyPrefix);
-
- ::BlockMoveData(&hostCName, &tempStr, 255); // host name, minus trailing “.”
- CtoPstr((char*) &tempStr);
- if (tempStr[tempStr[0]] == '.')
- tempStr[0]--;
- ConcatPStr(hostStr, tempStr);
-
- if (actualPort != defaultPort) { // port number if non-standard
- ::GetIndString(tempStr, STR__WindowStrings, Wstr_PortDelimiter);
- ConcatPStr(hostStr, tempStr);
- ::NumToString(actualPort, tempStr);
- ConcatPStr(hostStr, tempStr);
- }
-
- if (!sessionReady) { // right “)” if not ready
- ::GetIndString(tempStr, STR__WindowStrings, Wstr_NotReadySuffix);
- ConcatPStr(hostStr, tempStr);
- }
-
- }
-
- if (showFileName) { // “ : ” between file & host name
- ::GetIndString(tempStr, STR__WindowStrings, Wstr_Separator);
- ConcatPStr(fileStr, tempStr);
- }
- ConcatPStr(fileStr, hostStr);
- }
-
-
- // set the title
-
- if (showFileName || showHostName) // if neither file nor hostname are requested,
- SetWindowTitle(fileStr); // assume subclass will specify window titles
-
- }
-
-
- // -- error handling --
-
- //***********************************************************
-
- short CTCPEndpoint::TCPErrorAlert // protected method
- (OSErr err, // the error number
- long message, // message code (same as for ErrorAlert, see <TCLUtilities.cp>
- short alertID, // the ALRT/DITL resources to use
- short parm3) // the number to plug into ^3 in the error box
-
- // Display a customized message to indicate to the user that the connection failed.
- // This routine is copied from the TCL’s routine ErrorAlert.
-
- {
- Str255 errStr;
- Str255 hostStr;
- Str63 numStr;
- Str63 numStr3;
- short strIndex, strID;
-
- //! need to check to see if the rest of this works w/o TCL
-
- // silence any further messages
-
- #if TurboTCP_TCL
- gLastError = kSilentErr;
- #endif
-
-
- // see if anyone filled in the message field
-
- errStr[0] = 0;
- strIndex = message & 0xFFFF;
-
- if (strIndex > 0) {
- strID = message >> 16 & 0xFFFF;
- if (strID == 0)
- #if TurboTCP_TCL
- strID = STR_TCLfailMsgs; // use built-in messages
- #else
- strID = 0; //! TEMPORARY: hmmm... what to do
- #endif
- else
- #if TurboTCP_TCL
- strID += kUserFailMsgBase; // use user’s message STR#
- #else
- strID = 0; //! TEMPORARY: hmmm… what to do
- #endif
- ::GetIndString(errStr, strID, strIndex);
- }
-
-
- // if still no message, check for 'Estr' resource
-
- if (errStr[0] == 0) {
- StringHandle strH;
-
- strH = (StringHandle) ::GetResource('Estr', err);
- if (!strH)
- #if TurboTCP_TCL
- strH = ::GetString(STRosError2);
- #else
- strH = nil; //! TEMPORARY: not sure what to do
- #endif
- if (strH)
- CopyPStr(*strH, errStr);
- }
-
-
- // convert Mac error# and user’s host name to strings
-
- ::NumToString(err, numStr);
- ::NumToString(parm3, numStr3);
-
- ::BlockMoveData(&hostCName, &hostStr, 255);
- CtoPstr((char*) &hostStr);
- if (hostStr[hostStr[0]] == '.')
- hostStr[0]--;
-
- ::ParamText(errStr, numStr, hostStr, numStr3);
-
-
- // avoid infinite recursion in error handling by specifically
- // testing if the ALRT and DITL resources we need are there
-
- if ((::GetResource('ALRT', alertID) == nil) || (::GetResource('DITL', alertID) == nil)) {
- #if TurboTCP_TCL
- if (gError)
- gError->MissingResources();
- else
- #endif
- ::ExitToShell(); // nothing else we can do...
- }
-
-
- // show the error alert
-
- #if TurboTCP_TCL
- PositionDialog('ALRT', alertID); //! TEMPORARY: aarrrgghh!
- #endif
- ::InitCursor();
- return ::StopAlert(alertID, nil);
-
- }
-
-
- // -- TCP/DNR notification routines --
-
- //***********************************************************
-
- void CTCPEndpoint::HandleClosing // protected method
- (Boolean remoteClosing) // true if close initiated by remote host
-
- // Respond to notification that the session is being closed.
-
- {
- StateChanged();
- if (remoteClosing)
- RemoteClose();
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleOpened() // protected method
-
- // Respond to successful completion of a TCPPassiveOpen or TCPActiveOpen command.
-
- {
- actualPort = itsStream->itsRemotePort;
- StateChanged();
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleOpenFailed // protected method
- (OSErr theResultCode) // the reason for failure
-
- {
- TCPErrorAlert(theResultCode, 0L, ALRT_TCPOpenFailed, 1);
- RemoteClose();
- ThrowOSErr_(0);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleTCPError // protected method
- (OSErr theResultCode, // the result code
- short theCsCode) // the TCP command number that failed
-
- // Respond to failure of a TCP command that was not expected. Default method displays a
- // dialog, but takes no other action.
-
- {
- TCPErrorAlert(theResultCode, 0L, ALRT_TCPUnexpError, theCsCode);
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleTerminated // protected method
- (TCPTerminReason terminReason, // reason for termination (see TCP dev guide, p93)
- Boolean aboutToDispose) // true if TCP stream will now dispose of itself
-
- // Respond to session termination event.
-
- {
-
- // display error alert unless due to normal closure
-
- if ((terminReason != tcpTermAbort) && (terminReason != tcpTermClose))
- TCPErrorAlert(terminReason + ESTR_TerminBase, 0L, ALRT_TCPTerminated, 0);
-
-
- // retitle window & close if requested
-
- StateChanged();
- RemoteClose();
-
-
- // mark TCP stream as gone if appropriate
-
- if (aboutToDispose)
- itsStream = nil;
-
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleStrToAddr // protected method
- (struct hostInfo* theHostInfo) // the hostinfo record (see TCP dev guide, p70)
-
- // Respond to nofitication that a resolver StrToAddr call has completed. Most of this code
- // is written to complete the OpenUserHost method. If OpenUserHost was issued, this method
- // proceeds to establish a connection with the IP host which was named by the user.
-
- {
-
- // if OpenUserHost was issued, finish the job
-
- if ((pendingOpenByName) && (itsStream)) {
- if ((*theHostInfo).rtnCode == noErr) {
-
-
- // success, open the connection
-
- hostAddress = (*theHostInfo).addr[0];
- pendingOpenByName = false;
- itsStream->OpenConnection(false, hostAddress, pendingPortNumber, localPort);
-
-
- // if desired, get the canonical name
-
- if (useCName) {
- if ((*theHostInfo).cname[0] == 0) {
- itsResolver->DoAddrToName(hostAddress);
- pendingOpenByName = true;
- }
- else
- ::BlockMoveData(&(*theHostInfo).cname, &hostCName, 255);
- }
-
-
- // set the window title to reflect the name
-
- StateChanged();
-
- } else {
-
- // failed, issue alert & quit
-
- pendingOpenByName = false;
- HandleOpenFailed((*theHostInfo).rtnCode);
-
- } // if (...noErr)
- } // if (pendingOpenByName)
-
- }
-
-
- //***********************************************************
-
- void CTCPEndpoint::HandleAddrToName // protected method
- (struct hostInfo* theHostInfo) // the hostInfo record (see TCP dev guide, p75)
-
- // Respond to notification that a resolver AddrToName call has completed.
-
- {
-
- // if OpenUserHost was issued, grab the hostname
-
- if ((pendingOpenByName) && (useCName)) {
- if ((*theHostInfo).rtnCode == noErr) {
- pendingOpenByName = false;
- if ((*theHostInfo).cname[0])
- ::BlockMoveData(&(*theHostInfo).cname, &hostCName, 255);
- StateChanged();
- }
- }
-
- }
-